
#include "kernel.h"
#include "textmode/textmode.h"
#include "idt/idt.h"

#include "io/io.h"
#include "memory/memory.h"
#include "memory/heap/kheap.h"
#include "memory/paging/paging.h"
#include "disk/disk.h"
#include "utils/string.h"
#include "fs/pathparser.h"
#include "fs/file.h"
#include "config.h"
#include "gdt/gdt.h"
#include "task/tss.h"
#include "task/task.h"
#include "task/process.h"
#include "status.h"

static struct paging_4Gbyte_chunk* kernel_chunk = 0; 

void kernel_page()
{
    kernel_registers();
    paging_switch(kernel_chunk); 
}

struct gdt gdt_real[TESTOS_TOTAL_GDT_SEGMENTS];
struct tss tss;

void kernel_main() 
{
    struct gdt_structured gdt_structured[TESTOS_TOTAL_GDT_SEGMENTS] = 
    {
        { .base = 0x00, .limit = 0x00000000, .type = 0x00 }, // null segment
        { .base = 0x00, .limit = 0xFFFFFFFF, .type = 0x9A }, // kernel code segment 
        { .base = 0x00, .limit = 0xFFFFFFFF, .type = 0x92 }, // kernel data segment
        { .base = 0x00, .limit = 0xFFFFFFFF, .type = 0xF8 }, // user core segment
        { .base = 0x00, .limit = 0xFFFFFFFF, .type = 0xF2 }, // user data segment
        { 
            .base = (uint32_t)&tss, 
            .limit = sizeof(tss), 
            .type = 0xE9 
        }                                                    // TSS segment
    }; 

    // Initialize a textmode terminal 
    textmode_terminal_init(); 

    // Load the GDT
    memset(gdt_real, 0x00, sizeof(gdt_real)); 
    gdt_structured_to_gdt(gdt_real, gdt_structured, TESTOS_TOTAL_GDT_SEGMENTS); 
    gdt_load(gdt_real, sizeof(gdt_real)); 

    // Initialize a heap
    kheap_init();

    // Initialize the filesystems
    fs_init(); 

    // Search and initialize the disks
    disk_search_and_init(); 

    // Initialize an Interrupt Desceriptor Table
    idt_init();  

    // Setup the TSS
    memset(&tss, 0, sizeof(tss)); 
    tss.esp0 = 0x600000; // Where's kernel stack is located
    tss.ss0 = TESTOS_KERNEL_DATA_SELECTOR; 

    tss_load(0x28); // 0x28 is an offset where's GDT located

    // Setup paging
    kernel_chunk = paging_new_4Gbyte(PAGING_IS_WRITEABLE | PAGING_IS_PRESENT | PAGING_ACCESS_FROM_ALL); 
    paging_switch(kernel_chunk); 
    enable_paging(); 

    // Enable the system interrupts
    // enable_interrupts(); 

    struct process* process = 0; 
    const int res = process_load("0:/hello.bin", &process); 
    if (res != TESTOS_STATUS_OK) {
        panic("kernel_main(): failed to load blank.bin"); 
    }

    task_run_first_ever_task(); 
}

void panic(const char* msg)
{
    print_colored("[PANIC] ", TEXTMODE_COLOR_LIGHTRED);
    print_colored(msg, TEXTMODE_COLOR_LIGHTRED);
    print("\n"); 

    while (1) { }
}